/*
 * SmartPointer.h
 *
 *  Created on: Dec 17, 2011
 *      Author: rudi
 */

#ifndef SMARTPOINTER_H_
#define SMARTPOINTER_H_
#include <map>

/**
 * Reference counter for pointers
 *
 * @author Rudolf Heszele heszele@gmail.com
 * @version 1.0
 * @since 2012-03-08
 */
class PointerMapping
{
	public:
		static PointerMapping& getInstance() { static PointerMapping pointerMapping; return pointerMapping; }
		unsigned int& operator[](void* pointer) { return mPointerMap[pointer]; }

	private:
		PointerMapping(): mPointerMap() { /* Nothing to do */ };
		PointerMapping(const PointerMapping&);
		~PointerMapping() { /* Nothing to do */ };

		std::map<void*, unsigned int> mPointerMap;
};

/**
 * Smart pointer class for pointer handling
 *
 * @author Rudolf Heszele heszele@gmail.com
 * @version 1.0
 * @since 2012-03-08
 */
template<class T>
class SmartPointer
{
	public:
		/**
		 * The constructor takes the pointer to take care of as the argument
		 *
		 * @param pointer The pointer to handle
		 * @param isArray If it is true the pointer created with the array type of new (new[])
		 */
		SmartPointer(T* pointer = NULL, bool isArray = false);
		SmartPointer(const SmartPointer<T>& smartPointer);
		virtual ~SmartPointer();
		T& operator*();
		const T& operator*() const;
		T* operator->();
		const T* operator->() const;
		bool operator==(const SmartPointer<T>& smartPointer) const;
		bool operator==(T* pointer) const;
		SmartPointer<T>& operator=(const SmartPointer<T>& smartPointer);
		SmartPointer<T>& operator=(T* pointer);
		operator bool() const;
		operator T*();
		T* get();

	protected:
		/**
		 * If it is true, the pointer will be deleted as an array (delete[])
		 */
		bool mIsArray;

	private:
		/**
		 * Assigns the pointer to the object, increments the reference counter
		 */
		void assign(T* pointer);
		/**
		 * Separates the pointer from the object, decrements the reference counter.
		 * If the reference counter reaches zero, the pointer will be deleted.
		 */
		void release();

		T* mPointer;
};

/**
 * Helper class for single objects allocated on the heap
 *
 * This class has the same functionality as the SmartPointer, but it can help increase readability of the code
 * @author Rudolf Heszele heszele@gmail.com
 * @version 1.0
 * @since 2012-03-08
 */
template<class T>
class SimpleSmartPointer: public SmartPointer<T>
{
	public:
		/**
		 * The constructor takes the pointer to take care of as the argument
		 *
		 * @param pointer The pointer to handle
		 */
		SimpleSmartPointer(T* pointer = NULL): SmartPointer<T>(pointer, false) { /* Nothing to do yet */ }
		SimpleSmartPointer(const SimpleSmartPointer<T>& smartPointer): SmartPointer<T>(smartPointer) { /* Nothing to do yet */ }
		virtual ~SimpleSmartPointer() { /* Nothing to do yet*/ }
};

/**
 * Helper class for multiple objects allocated on the heap
 *
 * This class has the same functionality as the SmartPointer if the isArray is set to false in its constructor,
 * but it can help increase readability of the code.
 * @author Rudolf Heszele heszele@gmail.com
 * @version 1.0
 * @since 2012-03-08
 */
template<class T>
class ArraySmartPointer: public SmartPointer<T>
{
	public:
		/**
		 * The constructor takes the pointer to take care of as the argument
		 *
		 * @param pointer The pointer to handle
		 */
		ArraySmartPointer(T* pointer = NULL): SmartPointer<T>(pointer, true) { /* Nothing to do yet */ }
		ArraySmartPointer(const SimpleSmartPointer<T>& smartPointer): SmartPointer<T>(smartPointer) { /* Nothing to do yet */ }
		virtual ~ArraySmartPointer() { /* Nothing to do yet */ }
};

/*
 * SmartPointer class
 */

template<class T>
SmartPointer<T>::SmartPointer(T* pointer, bool isArray): mIsArray(isArray), mPointer(NULL)
{
	this->assign(pointer);
}

template<class T>
SmartPointer<T>::SmartPointer(const SmartPointer<T>& smartPointer): mIsArray(smartPointer.mIsArray), mPointer(NULL)
{
	this->assign(smartPointer.mPointer);
}

template<class T>
SmartPointer<T>::~SmartPointer()
{
	this->release();
}

template<class T>
T& SmartPointer<T>::operator*()
{
	return *mPointer;
}

template<class T>
const T& SmartPointer<T>::operator*() const
{
	return *mPointer;
}

template<class T>
T* SmartPointer<T>::operator->()
{
	return mPointer;
}

template<class T>
const T* SmartPointer<T>::operator->() const
{
	return mPointer;
}

template<class T>
bool SmartPointer<T>::operator==(const SmartPointer<T>& smartPointer) const
{
	return mPointer == smartPointer.mPointer;
}

template<class T>
bool SmartPointer<T>::operator==(T* pointer) const
{
	return mPointer == pointer;
}

template<class T>
SmartPointer<T>& SmartPointer<T>::operator=(const SmartPointer<T>& smartPointer)
{
	if(this != &smartPointer)
	{
		this->release();
		this->assign(smartPointer.mPointer);
	}

	return *this;
}

template<class T>
SmartPointer<T>& SmartPointer<T>::operator=(T* pointer)
{
	if(mPointer != pointer)
	{
		this->release();
		this->assign(pointer);
	}

	return *this;
}

template<class T>
SmartPointer<T>::operator bool() const
{
	return mPointer != NULL;
}

template<class T>
SmartPointer<T>::operator T*()
{
	return mPointer;
}

template<class T>
T* SmartPointer<T>::get()
{
	return mPointer;
}

/*
 * Private methods
 */

template<class T>
void SmartPointer<T>::assign(T* pointer)
{
	mPointer = pointer;
	PointerMapping::getInstance()[mPointer]++;
}

template<class T>
void SmartPointer<T>::release()
{
	PointerMapping::getInstance()[mPointer]--;
	if(0 == PointerMapping::getInstance()[mPointer])
	{
		if(false == mIsArray)
			delete mPointer;
		else
			delete[] mPointer;
	}
	mPointer = NULL;
}

#endif /* SMARTPOINTER_H_ */
